﻿#include "TcpConnectHandler.h"

#include "cJSON.h"

#include <fstream>

#include "reportstatusreq.h"



#include "appmonitortaskmanager.h"

#include "systemmonitormanager.h"

#include "commonresp.h"

#include "getapprunningstateres.h"

#include "getsystemrunningstateres.h"




const char magicNum[] =  { '@', '_', 'M', 'N', 'B', 'G', '_', '@' };
#define MAGC_NUM_LEN (sizeof(magicNum))

const int headLength = 20;

const int tlvMaxLenght = 1024 * 1024 * 20; //20M
const int MaxPdu = 1000;

#pragma optimize( "", off )

long TcpConnectHandler::connnectionCount = 0;

QMutex* TcpConnectHandler::connectionCountLock = new QMutex();


int findMagNum(char *buffer, int bufferLen)
{
	
	if (buffer == NULL || bufferLen < MAGC_NUM_LEN)
	{
        LOG_ERROR("invalid parameter, can't find magic num");
		return -1;
	}

	bool found = false;
	int i = 0;
	for (i = 0; i < (bufferLen - MAGC_NUM_LEN); i++) {
		bool eq = true;
		for (int j = 0; j < MAGC_NUM_LEN && eq; j++) {
			if (buffer[i + j] != magicNum[j]) {
				eq = false;
				break;
			}
		}

		if (eq) {
			found = true;
			break;
		}
		else
		{
			continue;
		}
	}

	if (found) {
		return i;
	}
	else {
		return -1;
	}

}

int sendDataToSocket(SOCKET connection, char * buffer, int bufferLen)
{
    LOG_DEBUG("send data buffer to socket with lenth:%d ", bufferLen);
	//LOG_PRINT_BUFFER(buffer, bufferLen);
	return send(connection, buffer, bufferLen, 0);
}

int sendHeadToSocket(SOCKET connection)
{
    LOG_DEBUG("send head out");
	char head[headLength];
	memset(head, 0, headLength);
	strncpy(head, magicNum, MAGC_NUM_LEN);

	return sendDataToSocket(connection, head, headLength) == headLength ? f_success : f_fail;
}

int sendIntValueToSocket(SOCKET connection, int value)
{

    LOG_DEBUG("send int value to socket:%d", value);
	byte buffer[TLV_INT_LEN];
	memset(buffer, 0, TLV_INT_LEN);
	buffer[0] = (byte)(value >> 24);
	buffer[1] = (byte)(value >> 16);
	buffer[2] = (byte)(value >> 8);
	buffer[3] = (byte)(value);

    int ret = sendDataToSocket(connection, (char *)(buffer), TLV_INT_LEN);
    if(ret != TLV_INT_LEN)
    {
        ret = ret;
    }
    return ret;
}

int sendTlvToSocket(SOCKET connection, const TLV * const tlv)
{
	if (sendIntValueToSocket(connection, tlv->tag) == TLV_INT_LEN
		&& sendIntValueToSocket(connection, tlv->length) == TLV_INT_LEN
		&& sendDataToSocket(connection, tlv->buffer, tlv->length) == tlv->length
		)
	{
		return f_success;
	}
	else
	{
		return f_fail;
	}
}

int sentTlvsToSocket(SOCKET connection, list<TLV *> tlvs)
{
	list<TLV *>::iterator iter;
	for (iter = tlvs.begin(); iter != tlvs.end(); iter++)
	{
		if (sendTlvToSocket(connection, *iter) != f_success)
		{
			return f_fail;
		}
	}
	return f_success;
}

int readDataFromSocket(SOCKET connection, char * buffer, int bufferLen)
{
	int offSet = 0;
	int wanted = bufferLen;
	char * buff = buffer;

	if (buff != NULL) {
		while (wanted > 0) {
			int readed = 0;
			if (wanted > MaxPdu) {
			
				readed = recv(connection, buff, MaxPdu, 0);

			}
			else {
				readed = recv(connection, buff, wanted, 0);
			}

			if (readed <= 0) {
                LOG_INFO("读取socket数据失败，返回值：%d, 可能对方关闭了连接", readed);
				return readed;
			}
			else
			{
				wanted = wanted - readed;
				offSet = offSet + readed;
				buff += readed;
                LOG_DEBUG("从Socket读取到长度为:%d的数据, 剩余读取长度为：%d 的数据", readed, wanted);
			}
		}
	}

	//LOG_PRINT_BUFFER(buffer, offSet);
	return offSet;
}



void TcpConnectHandler::initSocket()
{
	static bool inited = false;
	if (inited == false)
	{
		WSADATA wsaData;
		WSAStartup(MAKEWORD(2, 2), &wsaData);
		inited = true;
	}
}




TcpConnectHandler* TcpConnectHandler::connect(SString ip, int port)
{

	TcpConnectHandler::initSocket();

	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sclient == INVALID_SOCKET)
	{
        LOG_ERROR("invalid socket !");
		return NULL;
	}

	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(port);
	serAddr.sin_addr.s_addr = inet_addr(ip.c_str());
	memset(serAddr.sin_zero, 0x00, 8);

    int iTimeOut = 3000;
    setsockopt(sclient,SOL_SOCKET,SO_RCVTIMEO,(char*)&iTimeOut,sizeof(iTimeOut));
    setsockopt(sclient,SOL_SOCKET,SO_SNDTIMEO,(char*)&iTimeOut,sizeof(iTimeOut));


	if (::connect(sclient, (struct sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
        LOG_ERROR("try to connect to tcp server:%s, port:%d failed", ip.c_str(), port);
		closesocket(sclient);
		return NULL;
	}
	else
	{
        LOG_INFO("try to connect to tcp server:%s, port:%d  success", ip.c_str(), port);
	}

    TcpConnectHandler *ret = new TcpConnectHandler(sclient);
    ret->remoteIP = ip;
    ret->remotePort = port;
    return ret;
}

TcpConnectHandler::TcpConnectHandler(SOCKET connection)
{
    TcpConnectHandler::connectionCountLock->lock();
    TcpConnectHandler::connnectionCount += 1;
    TcpConnectHandler::connectionCountLock->unlock();
	this->connection = connection;

    this->remoteIP = "unknown";
    this->remotePort = -1;
}


TcpConnectHandler::~TcpConnectHandler()
{
	//如果没有关闭socket，这里强制关闭下
	if (this->connection > 0)
	{
		closesocket(this->connection);
        this->connection = -1;
        TcpConnectHandler::connectionCountLock->lock();
        TcpConnectHandler::connnectionCount -= 1;
        TcpConnectHandler::connectionCountLock->unlock();
	}
}


int TcpConnectHandler::start()
{

    LOG_INFO("start reading request");
	//step one, 从socket中获取请求
	while (connectionIsGood)
	{
		BaseMsg * req = readMsg();
		if (req)
		{
            if(req->msgType.compare(MSG_TYPE_COMMON_REQ) == 0)
            {
                CommonReq * commonReq = (CommonReq *)req;
                if(commonReq->type == CommonReq_type_getapprunningstate)
                {
                    handleGetAppRunningStateCommonReq(commonReq);
                }
                else if(commonReq->type == CommonReq_type_getsystemrunningstate)
                {
                    handleGetSysRunningStateCommonReq(commonReq);
                }
                else
                {
                    LOG_ERROR("something wrong, un supported common msg with type:%d", commonReq->type);
                    SafeDeleteObj(commonReq);
                    connectionIsGood = false;
                }
            }
            else if (req->msgType.compare(MSG_TYPE_APP_UPDATE_REQ) == 0)
            {
                AppUpdateReq * appUpdateReq = (AppUpdateReq *)req;
                handleAppUpdateReq(appUpdateReq);
                SafeDeleteObj(appUpdateReq);
            }
            else
            {
                LOG_ERROR("something wrong, un supported msg with type:%s", req->msgType.c_str());
                SafeDeleteObj(req);
                connectionIsGood = false;
            }
		}
		else
		{
            LOG_INFO("read one null msg, remote tcp connection reset");
			connectionIsGood = false;
		}
	}


    return f_success;
}

void TcpConnectHandler::handleAppUpdateReq(AppUpdateReq *req)
{
    QString failReason;

    CommonResp * res = NULL;

    if(f_success == gAppMonitorTaskManager.handleAppUpdateReq(req, failReason))
    {
        res = new CommonResp(CommonRespResult_success, "success");
    }
    else
    {
        res = new CommonResp(CommonRespResult_fail, failReason.toStdString());
    }

    sendMsg(res);

    SafeDeleteObj(res);
}


void TcpConnectHandler::handleGetAppRunningStateCommonReq(CommonReq *req)
{
    SafeDeleteObj(req);

    GetAppRunningStateRes res;
    res.apps = gAppMonitorTaskManager.getCurrentRunningInfos();
    sendMsg(&res);
}

void TcpConnectHandler::handleGetSysRunningStateCommonReq(CommonReq *req)
{
    SafeDeleteObj(req);

    GetSystemRunningStateRes res;
    foreach(double cpu,gSystemMonitorManger.lastCPUUsages){
        float cpuFloat = cpu;
        res.lastCPUUsages.push_back(cpuFloat);
    }

    foreach (MEMORYSTATUSEX mem, gSystemMonitorManger.lastMEMUsages) {
        float memFloat = mem.dwMemoryLoad;
        res.lastMEMUsages.push_back(memFloat);
    }

    foreach (DiskInfo disk, gSystemMonitorManger.lastDiskInfo) {
        DiskInfoTlvEncodedObj obj;
        obj.disk = disk.disk;
        obj.freeMB = disk.freeMB;
        obj.totalMB = disk.totalMB;
        res.lastDiskInfo.push_back(obj);
    }

   if(sendMsg(&res) == f_success){
       LOG_INFO ("sendMsg_GetSystemRunningStateRes_Success");
   }else{
       LOG_ERROR ("sendMsg_GetSystemRunningStateRes_Fail");
   }
}

vector<float> decodeFeature(const char *value)
{
	vector<float> feature;

	if (value && strlen(value) > 0)
	{
		cJSON *featureJSON = cJSON_Parse(value);

		if (featureJSON)
		{
			int featureSize = cJSON_GetArraySize(featureJSON);

			for (int i = 0; i < featureSize; i++) {
				cJSON *item = cJSON_GetArrayItem(featureJSON, i);
				float f = (float)item->valuedouble;
				feature.push_back(f);

			}
		}

		if (featureJSON)
			cJSON_Delete(featureJSON);
	}


	return feature;
}



BaseMsg * TcpConnectHandler::readMsg()
{

	SString msgType;
	BaseMsg * msg = NULL;
	if (f_success == readMagNum() && f_success == readMsgType(msgType))
    {
        if(msgType.compare(MSG_TYPE_REPORT_STATUS_REQ) == 0)
        {
            msg = new ReportStatusReq();
        }
        else if (msgType.compare(MSG_TYPE_COMMON_RESP) == 0)
        {
            msg = new CommonResp();
        }
        else if (msgType.compare(MSG_TYPE_COMMON_REQ) == 0)
        {
            msg = new CommonReq();
        }
        else if (msgType.compare(MSG_TYPE_GET_APP_RUNNING_STATE_RES) == 0)
        {
            msg = new GetAppRunningStateRes();
        }
        else if (msgType.compare(MSG_TYPE_GET_SYSTEM_RUNNING_STATE_RES) == 0)
        {
            msg = new GetSystemRunningStateRes();
        }
        else
        {
            LOG_ERROR("invalid msg type:%s, not supported yet", msgType.c_str());
            return NULL;
        }



		if (msg != NULL)
		{
			TLV * nextTlv = NULL;
			while (readNextTlv(nextTlv) == f_success && nextTlv != NULL && !nextTlv->isEndTlv())
			{
				msg->decodeParametersFromTlv(nextTlv);
				SafeDeleteObj(nextTlv);
			}

			SafeDeleteObj(nextTlv);
			return msg;
		}
		else
		{
            LOG_ERROR("req is null, may msgtype:%s decode not finished yet?", msgType.c_str());
			return NULL;
		}
	}
	else
	{
        LOG_INFO("connection reseted by remote");
		return NULL;
	}


}


int TcpConnectHandler::sendMsg(BaseMsg * msg)
{

	if (sendHeadToSocket(connection) == f_success)
	{
		list<TLV *> tlvs; 
		if (msg->encodeIntoWholeTlvs(tlvs) == f_success && sentTlvsToSocket(connection, tlvs) == f_success)
		{
			releaseTlvs(tlvs);
			return f_success;
		}
		else
		{
			releaseTlvs(tlvs);
            LOG_ERROR("try to send out msg into tlvs failed");
			return f_fail;
		}
	}
	else
	{
        LOG_ERROR("try to send out head failed, send msg failed");
		return f_fail;
	}

}

int readInt32FromSocket(SOCKET connection, int & intValue)
{
	char buffer[TLV_INT_LEN];
	if (readDataFromSocket(connection, buffer, TLV_INT_LEN) == TLV_INT_LEN)
	{
		intValue = 0;
		byte bLoop;

		for (int i = 0; i < TLV_INT_LEN; i++) {
			bLoop = buffer[i];
			intValue = intValue * 256 + bLoop;
		}

        LOG_DEBUG("read one int32 from socket with value:%d", intValue);
		return f_success;
	}
	else
	{
		return f_fail;
	}
}


int TcpConnectHandler::readMagNum()
{
	char head[headLength];
	
	int readed;
	int iMagNumIndex;
	if ((readed = readDataFromSocket(connection, head, headLength)) == headLength)
	{
		iMagNumIndex = findMagNum(head, headLength);
		while (iMagNumIndex == -1)
		{
            LOG_WARN("获取MagicNum失败，继续读取");
			for (int end = headLength - MAGC_NUM_LEN; end < headLength; end++) {
				int begin = 0;
				head[end] = head[begin];
				begin++;
			}

			readed = readDataFromSocket(connection, head + MAGC_NUM_LEN, headLength - MAGC_NUM_LEN);
			if (readed == headLength - MAGC_NUM_LEN)
			{
				iMagNumIndex = findMagNum(head, headLength);
			}
			else
			{
                LOG_ERROR("socket error, readMagNum failed");
				return f_fail;
			}
		}
        LOG_INFO("read magic num success at index:%d, on income one new msg", iMagNumIndex);
		return f_success;
	}

	else
	{
        LOG_INFO("read head with return value:%d, not eqaul to headlenth:%d, read magNum failed", readed, headLength);
		return f_fail;
	}
}

int TcpConnectHandler::readNextTlv(TLV *& tlv)
{
	SafeDeleteObj(tlv);

	int tag;
	int length;
	if (f_success == readInt32FromSocket(connection, tag) && f_success == readInt32FromSocket(connection, length))
	{
		tlv = new TLV(tag, length);
		if (readDataFromSocket(connection, tlv->buffer, tlv->length) != tlv->length)
		{
			SafeDeleteObj(tlv);
			return f_fail;
		}
		else
		{
			return f_success;
		}
	}
	else
	{
		return f_fail;
	}
}



int TcpConnectHandler::readMsgType(SString &msgType)
{
	TLV * tlv = NULL;
	if (f_success == readNextTlv(tlv))
	{
		if (tlv->isMsgTypeTlv())
		{
			msgType = tlv->buffer;
			SafeDeleteObj(tlv);
			return f_success;
		}
		else
		{
			SafeDeleteObj(tlv);
            LOG_ERROR("read msg type failed, tag is not msgtype");
			return f_fail;
		}
		
	}
	else
	{
		SafeDeleteObj(tlv);
        LOG_ERROR("read msg type failed, read next tlv failed");
		return f_fail;
	}
}


#pragma optimize( "", on ) 

int sendMsgWithCommonResp(BaseMsg *msg, QString ip, int port, QString &failReason)
{
    TcpConnectHandler * connect = TcpConnectHandler::connect(ip.toStdString(), port);
    if(connect == NULL)
    {
        failReason = "connect to ip:" + ip + ", port:" + int2QStr(port) + " failed";
        return f_fail;
    }
    else
    {
        if(connect->sendMsg(msg) != f_success)
        {
            SafeDeleteObj(connect);
            failReason = "send msg with type:" + QString::fromStdString(msg->msgType) + " to ip:" + ip + ", port:" + int2QStr(port) + " failed";
            return f_fail;
        }
        else
        {
            CommonResp * res = (CommonResp *)connect->readMsg();
            if(res == NULL)
            {
                SafeDeleteObj(connect);
                failReason = "read CommonResp from ip:" + ip + ", port:" + int2QStr(port) + " failed";
                return f_fail;
            }
            else
            {
                if(res->code == CommonRespResult_success)
                {
                    SafeDeleteObj(connect);
                    SafeDeleteObj(res);
                    failReason = QString::fromStdString("success!");
                    return f_success;
                }
                else
                {
                    SafeDeleteObj(connect);
                    SafeDeleteObj(res);
                    failReason = QString::fromStdString(res->result);
                    return f_fail;
                }
            }
        }
    }
}
